package mss

import (
	"fmt"
	"mss/internal/salvedev"
	"mss/internal/utils"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/fsnotify/fsnotify"
	"gopkg.in/yaml.v2"
)

func watchConfig(path string) {
	watcher, _ := fsnotify.NewWatcher()
	defer watcher.Close()

	watcher.Add(path)

	for event := range watcher.Events {
		if event.Op&fsnotify.Create == fsnotify.Create {
			fmt.Fprintf(os.Stdout, "[config] 新增配置文件:%v\n", event.Name)
			go func(filename string) {
				time.Sleep(time.Second) // 等待文件写入完成
				err := onAddConfigFile(filename)
				if err != nil {
					fmt.Fprintf(os.Stderr, "[config] 响应新增事件失败:%v\n", err)
				} else {
					fmt.Fprintf(os.Stdout, "[config] 响应新增事件成功\n")
				}
			}(event.Name)
		}
		if event.Op&fsnotify.Write == fsnotify.Write { //可能会连续触发多次
			fmt.Printf("[config] 更新配置文件:%v\n", event.Name)
			go func(filename string) {
				if _, ok := mutexUpdatingDevice.Load(filepath.Base(filename)); !ok {
					mutexUpdatingDevice.Store(filepath.Base(filename), &sync.Mutex{})
				}
				m, _ := mutexUpdatingDevice.Load(filepath.Base(filename))
				if !m.(*sync.Mutex).TryLock() {
					return
				}
				defer m.(*sync.Mutex).Unlock()

				time.Sleep(time.Second * 2) // 等待文件写入完成
				err := onUpdateConfigFile(filename)
				if err != nil {
					fmt.Fprintf(os.Stderr, "[config] 响应更新事件失败:%v\n", err)
				} else {
					fmt.Fprintf(os.Stdout, "[config] 响应更新事件成功\n")
				}
			}(event.Name)
		}
		if event.Op&fsnotify.Remove == fsnotify.Remove || event.Op&fsnotify.Rename == fsnotify.Rename {
			fmt.Printf("[config] 删除或重命名配置文件:%v\n", event.Name)
			go func(filename string) {
				time.Sleep(time.Second) // 等待文件写入完成
				err := onDelConfigFile(filename)
				if err != nil {
					fmt.Fprintf(os.Stderr, "[config] 响应删除或重命名事件失败:%v\n", err)
				} else {
					fmt.Fprintf(os.Stdout, "[config] 响应删除或重命名事件成功\n")
				}
			}(event.Name)
		}
	}
}

// onDelConfigFile 删除配置文件
func onDelConfigFile(filename string) error {
	// 找到老的对象，并删除之

	var old *BoostDevice
	devices.Range(func(key, value any) bool {
		d2 := value.(*BoostDevice)
		if d2.filename == filepath.Base(filename) {
			old = d2
			return false
		}
		return true // 继续遍历
	})

	if old == nil {
		return fmt.Errorf("未找到对应的设备对象:%s", filename)
	}

	devices.Delete(old.Key())

	return nil
}

// onAddConfigFile 新增配置文件
func onAddConfigFile(filename string) error {
	//
	// 反序列化对象
	//

	data, err := os.ReadFile(filepath.Join(utils.CurrentExePath(), filename))
	if err != nil {
		return err
	}

	d := &BoostDevice{}
	err = yaml.Unmarshal(data, d)
	if err != nil {
		return err
	}

	if _, ok := devices.Load(d.Key()); ok {
		return fmt.Errorf("TCP和salveAddr对(%s)已存在", d.Key())
	}

	d.filename = filepath.Base(filename)
	d.salvedev = salvedev.NewModbusSlave(d.SalveAddr)

	//
	// 如果对应的TCP未启动，则启动
	//

	running := false
	devices.Range(func(key, value any) bool {
		d2 := value.(*BoostDevice)
		if d2.TcpPort == d.TcpPort {
			running = true
			return false
		}
		return true // 继续遍历
	})

	if !running {
		go tcpListening(d.TcpPort)
	}

	//
	//加入对象池
	//

	devices.Store(d.Key(), d)

	return nil
}

// onUpdateConfigFile 更新配置文件
func onUpdateConfigFile(filename string) error {
	//
	// 找到老的对象，并删除之
	//

	var old *BoostDevice
	devices.Range(func(key, value any) bool {
		d2 := value.(*BoostDevice)
		if d2.filename == filepath.Base(filename) {
			old = d2
			return false
		}
		return true // 继续遍历
	})

	if old != nil {
		devices.Delete(old.Key())
	}

	//
	// 反序列化对象
	//

	data, err := os.ReadFile(filepath.Join(utils.CurrentExePath(), filename))
	if err != nil {
		return err
	}

	d := &BoostDevice{}
	err = yaml.Unmarshal(data, d)
	if err != nil {
		return err
	}

	if _, ok := devices.Load(d.Key()); ok {
		return fmt.Errorf("TCP和salveAddr对(%s)已存在", d.Key())
	}

	d.filename = filepath.Base(filename)
	d.salvedev = salvedev.NewModbusSlave(d.SalveAddr)

	//
	// 如果对应的TCP未启动，则启动
	//

	running := false
	devices.Range(func(key, value any) bool {
		d2 := value.(*BoostDevice)
		if d2.TcpPort == d.TcpPort {
			running = true
			return false
		}
		return true // 继续遍历
	})

	if !running {
		go tcpListening(d.TcpPort)
	}

	//
	//加入对象池
	//

	devices.Store(d.Key(), d)

	return nil
}
